package com.qlogic.commons.platform.impls.dal.toplink;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import javax.naming.Context;
import javax.sql.DataSource;

import com.qlogic.commons.platform.ServiceInitializationException;

import oracle.toplink.platform.database.DatabasePlatform;
import oracle.toplink.jndi.JNDIConnector;
import oracle.toplink.publicinterface.Descriptor;
import oracle.toplink.sessions.Connector;
import oracle.toplink.sessions.DatabaseLogin;
import oracle.toplink.sessions.Project;
import oracle.toplink.threetier.ClientSession;
import oracle.toplink.threetier.ServerSession;
import oracle.toplink.tools.workbench.XMLProjectReader;

public class ToplinkServer {

	private static final long serialVersionUID = 5621715077631326504L;
	
	public static final String MAIN_PROJECT = "__mainProject__";

	protected String [] jars;
	
	protected Hashtable j2eeEnv;
	
	protected String dataSource;
	
	protected File mappingsDir;
	
	protected Project mainProject = new Project ();
	
	protected Hashtable projects = new Hashtable ();
	
	private boolean firstMapping = true;
	
	protected ServerSession session;

	protected String jassPassword;

	protected String jaasUser;
	
	protected boolean debug;
	
	protected boolean useExternalConnectionPooling = true;
	
	protected boolean useNativeSequencing;
	
	protected Connector connector;
	
	protected DatabasePlatform platform;
	
	private long sessionsCount;
	
	public ToplinkServer () {
	}
	
	public void initialize () throws ServiceInitializationException {
		Context ctx = null;
		try {
			ctx = new javax.naming.InitialContext (j2eeEnv);
			DataSource ds = (DataSource)ctx.lookup (dataSource);
			connector = new JNDIConnector (ds);
			loadMappings (mappingsDir);
			if (jaasUser != null) {
				session.login (jaasUser, jassPassword);
			} else {
				session.login ();
				if (debug) {
					session.logMessages();
				}
			}
			System.out.println("Server Session Instance [" + session + "]");
		} catch (Exception e) {
			throw new ServiceInitializationException (
					"Enable to get a database server session due to : ", e);
		}
	}

	public void loadMappings (File dirOrFile) throws ServiceInitializationException, IOException {
		loadMappings (dirOrFile, null);
	}
	
	public void loadMappings (File dirOrFile, ClassLoader classLoader) 
	throws ServiceInitializationException, IOException {
		if (!dirOrFile.exists()) {
			throw new ServiceInitializationException ("Directory [" + dirOrFile	+ "] does not exist");
		}
		if (!dirOrFile.canRead()) {
			throw new ServiceInitializationException ("Can not read " + dirOrFile);
		}
		if (dirOrFile.isFile ()) {
			loadMapping (dirOrFile, classLoader);
		} else {
			File[] filesOrDirs = dirOrFile.listFiles ();
			for (int i = 0; i < filesOrDirs.length; i++) {
				if (filesOrDirs [i].isFile ()) {
					if (!filesOrDirs [i].getName ().endsWith (".xml")) {
						continue;
					}
					loadMapping (filesOrDirs [i], classLoader);
				} else {
					System.out.println("Loading Directory Mappings [" + filesOrDirs [i].getAbsolutePath() + "]");
					loadMappings (filesOrDirs[i], classLoader);
				}
			}
		}
	}
	
	public void loadMapping (File file) throws ServiceInitializationException, IOException {
		loadMapping (file, null);
	}
	public void loadMapping (File file, ClassLoader classLoader) throws ServiceInitializationException, IOException {
		System.out.println("Loading Mapping [" + file.getAbsolutePath () + "]");
		FileReader reader = new FileReader (file); 
		if (classLoader == null) {
			classLoader = Thread.currentThread().getContextClassLoader();
		}
		if (firstMapping) {
			mainProject.setName (MAIN_PROJECT);
			DatabaseLogin databaseLogin = new DatabaseLogin();
			databaseLogin.setPlatform (platform);
			if (useExternalConnectionPooling) {
				databaseLogin.useExternalConnectionPooling ();
			}
			System.out.println ("platform " + platform);
			DatabaseLogin.setXMLParserJARFileNames (jars);
			for (int i = 0; i < jars.length; i++) {
				System.out.println ("jar : " + jars [i]);
			}
			mainProject = XMLProjectReader.read (reader, classLoader);
			mainProject.setLogin (databaseLogin);
			projects.put (mainProject.getName (), getProjectClasses (mainProject));
			System.out.println ("mainProject " + mainProject);
			System.out.println ("login " + mainProject.getLogin ());
			mainProject.getLogin ().setConnector (connector);
			if (useNativeSequencing) {
				mainProject.getLogin().useNativeSequencing ();
				mainProject.getLogin().setSequencePreallocationSize (1);
			}
			session = (ServerSession) mainProject.createServerSession ();
			firstMapping = false;
		} else {
			Project project = XMLProjectReader.read (reader, classLoader);
			if (project.getName () == null || project.getName ().trim ().equals ("")) {
				throw new ServiceInitializationException (file.getAbsolutePath () + " no name project");
			}
			if (projects.contains (project.getName ().trim ())) {
				throw new ServiceInitializationException (file.getAbsolutePath () + " project already loaded");
			}
			projects.put (project.getName ().trim (), getProjectClasses (project));
			session.addDescriptors (project);
		}
	}
	
	public void unload (String projectName) {
		Vector projectClasses = (Vector)projects.get (projectName.trim ());
		for (int i = 0; i < projectClasses.size (); i++) {
			Class descClass = (Class)projectClasses.elementAt (i);
			Descriptor descriptor = session.getDescriptor (descClass);
			session.getDescriptors ().remove (descClass);
			session.getProject().getOrderedDescriptors ().remove (descriptor);
			if (descriptor.getAlias () != null) {
				session.getAliasDescriptors ().remove (descriptor.getAlias ());
			}
		}
		projects.remove (projectName);
	}
	
	private Vector getProjectClasses (Project project) {
		Vector classes = null;
		Map map = project.getDescriptors ();
		Set keySet = map.keySet ();
		if (!keySet.isEmpty ()) {
			classes = new Vector ();
			Iterator iterator = keySet.iterator ();
			while (iterator.hasNext ()) {
				classes.add (iterator.next ());
			}
		}
		return classes;
	}
	
	public String[] getJars() {
		return jars;
	}

	public void setJars(String[] jars) {
		this.jars = jars;
	}

	public Hashtable getJ2eeEnv() {
		return j2eeEnv;
	}

	public void setJ2eeEnv(Hashtable env) {
		j2eeEnv = env;
	}

	public String getDataSource() {
		return dataSource;
	}

	public void setDataSource(String dataSource) {
		this.dataSource = dataSource;
	}

	public File getMappingsDir() {
		return mappingsDir;
	}

	public void setMappingsDir(File mappingsDir) {
		this.mappingsDir = mappingsDir;
	}

	public String getJaasUser() {
		return jaasUser;
	}

	public void setJaasUser(String jaasUser) {
		this.jaasUser = jaasUser;
	}

	public String getJassPassword() {
		return jassPassword;
	}

	public void setJassPassword(String jassPassword) {
		this.jassPassword = jassPassword;
	}

	public ClientSession newClientSession () {
		ClientSession clientSession = session.acquireClientSession();
		sessionsCount++;
		return clientSession;
	}

	public void releaseClientSession (ClientSession clientSession) {
		if (clientSession == null)
			return;
		clientSession.release();
		sessionsCount--;
	}

	public ServerSession getSession() {
		return session;
	}

	public DatabasePlatform getPlatform() {
		return platform;
	}

	public void setPlatform(DatabasePlatform platform) {
		this.platform = platform;
	}

	public boolean isDebug() {
		return debug;
	}

	public void setDebug(boolean debug) {
		this.debug = debug;
	}

	public boolean isUseNativeSequencing() {
		return useNativeSequencing;
	}

	public void setUseNativeSequencing(boolean useNativeSequencing) {
		this.useNativeSequencing = useNativeSequencing;
	}
}
